/*
 * Copyright (C) 2006 Roman Krashanitsa
 *
 * @author Roman Krashanitsa
 *
 * This file is part of paparazzi.
 *
 * paparazzi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * paparazzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with paparazzi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */

/*  <message name="SVINFO" id="25">
 *   <field name="chn" type="uint8"/>
 *   <field name="SVID" type="uint8"/>
 *   <field name="Flags" type="uint8"/>
 *   <field name="QI" type="int8"/>
 *   <field name="CNO" type="uint8" unit="dbHz"/>
 *   <field name="Elev" type="int8" unit="deg"/>
 *   <field name="Azim" type="int16" unit="deg"/>
 * </message>
*/
package pprz.components;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Scrollbar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.HashMap;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.event.EventListenerList;

import net.jscience.math.MathFP;
import pprz.data_components.Message;
import pprz.data_components.events.DownlinkMessageEvent;
import pprz.data_components.events.DownlinkMessageEventListener;

public class Gps extends Container
implements DownlinkMessageEventListener, 
ComponentListener, AdjustmentListener, ActionListener  {

	static final long serialVersionUID=1;
	
		class SvInfo extends Component {

			protected static final long serialVersionUID = 2L;
			protected static final int componentHeight=20;
			public static final int maxCno = 60; //dBHz
			
			protected int chn; //channel number, range 0..NCH-1
			protected int svid; //Satellite ID
			protected int flags; 	//Bitmask, made up of the following bit values 
								//	0x01 = SV is used for navigation
								//	0x02 = Differential correction data is available for this SV
								//	0x04 = Orbit information is available for this SV (Ephemeris or Almanach)
								//	0x08 = Orbit information is Ephemeris
								//	0x10 = SV is unhealthy / shall not be used
								//	0x20 = reserved
								//	0x40 = reserved
								//	0x80 = reserved 
			protected int qi;		//Signal Quality indicator (range 0..7). The following list shows the meaning of the different QI values: 
								//	0: This channel is idle
								// 	1, 2: Channel is searching
								//	3: Signal detected but unusable
								//	4: Code Lock on Signal
								//	5, 6: Code and Carrier locked
								//	7: Code and Carrier locked, receiving 50bps data 
			protected int cno;	//Carrier to Noise Ratio (Signal Strength), dbHz
			protected int elev;	//Elevation in integer degrees 
			protected int azim;	//Azimuth in integer degrees 
			
		
			public SvInfo() {
				super();
				this.setSize(20, componentHeight);
				this.setPreferredSize(new Dimension(20, componentHeight));
				this.setMaximumSize(new Dimension(20000, componentHeight));
				this.setMinimumSize(new Dimension(10, componentHeight));
			}
			
			public void set(int chn, int svid, int flags, int qi,int cno, int elev, int azim)
			{
				this.chn=chn;
				this.svid=svid;
				this.flags=flags;
				this.qi=qi;
				this.cno=cno;
				this.elev=elev;
				this.azim=azim;
			}
			

			public int getChn() {return chn;}
			public int getSvid(){return svid;}
			public int getFlags(){return flags;}
			public int getQi(){return qi;}
			public int getCno(){return cno;}
			public int getElev(){return elev;}
			public int getAzim(){return azim;}
			
			public void draw(Graphics g)
			{
				int w=this.getWidth();
				int h=this.getHeight();
				int width=(int)(w*(((float)cno)/maxCno));
				Graphics2D g2=(Graphics2D)g;
				g2.setColor(new Color(Color.HSBtoRGB(0.3f-qi/7.f*0.3f, 0.9f, 1.f)));
				g2.fillRect(0, 0, width, h);
				g2.setColor(Color.black);
				g2.setFont(new Font("SansSerif",Font.PLAIN,11));
				int ypos=g2.getFontMetrics().getHeight();
				g2.drawString("str:"+String.valueOf(cno)+"dBHz~elev:"+String.valueOf(elev)+"deg", 2, ypos);
			}
			
			public void paint(Graphics g) {
				draw(g);
			}

			public float getAlignmentY()
			{
				return Component.TOP_ALIGNMENT;
			}
			
		}


		class SvInfoPanel extends Container {
			
			static final long serialVersionUID=1;
			static final int padding=3;
			protected HashMap<Integer,SvInfo> sv;			
			
			public SvInfoPanel()
			{
				super();
				alloc();
				initialize();
			}
			
			protected void alloc()
			{
				sv=new HashMap<Integer,SvInfo>();
			}
			
			protected void initialize()
			{
				setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));	
			}
			
			public void put(Integer svid, SvInfo s )
			{
				sv.put(svid, s);
				this.add(s);
				this.add(Box.createRigidArea(new Dimension(0,padding)));
				this.setSize(new Dimension(getWidth(),sv.size()*(s.getHeight()+padding)));
			}
			public SvInfo get(Integer svid )
			{
				return sv.get(svid);
			}
			
			public boolean containsKey(Integer svid )
			{
				return sv.containsKey(svid);
			}
			
			public boolean containsValue(SvInfo o )
			{
				return sv.containsValue(o);
			}
			
			public int count() {return sv.size();}			
		}
		
		protected HashMap<Integer,SvInfoPanel> panels;
		protected Scrollbar scroller;
		protected int selected;
		protected ActionEvent e;
		protected EventListenerList listenerList = new EventListenerList();  
		
		public Gps() {
			super();
			alloc();
			initialize();
		}

		/**
		 * This method initializes this
		 * 
		 * @return void
		 */
		protected void alloc() {
			this.e=new ActionEvent(this,0,null);
			this.panels=new HashMap<Integer, SvInfoPanel>();
			this.scroller=new Scrollbar();
		}

		protected void initialize()
		{
			this.setLayout(null);
			this.addComponentListener(this);
			scroller.setFocusable(false);
			scroller.setMinimum(0);
			scroller.setMaximum(0);
			scroller.setUnitIncrement(10);
			scroller.addAdjustmentListener(this);
			this.add(scroller);
		}
		
		public int getSelected() {return selected;}

		public void messageEvent(DownlinkMessageEvent ev) {
			Message m = ev.getMessage();
			int msgid=m.id();
			int acid=m.acRec().get_id();
			if (msgid!=25) return; //skip it
			//System.out.println("SVinfo received.");
			int svid=(int)MathFP.toLong(m.getFields().get(1)[0]);
			if (!panels.containsKey(acid))
			{
				addPanel(acid);
			}
			SvInfoPanel panel=panels.get(acid);
			if (!panel.containsKey(svid)) panel.put(svid,new SvInfo());
			SvInfo s=panel.get(svid);
			s.set((int)MathFP.toLong(m.getFields().get(0)[0]), 
				  (int)MathFP.toLong(m.getFields().get(1)[0]), 
				  (int)MathFP.toLong(m.getFields().get(2)[0]), 
				  (int)MathFP.toLong(m.getFields().get(3)[0]), 
				  (int)MathFP.toLong(m.getFields().get(4)[0]), 
				  (int)MathFP.toLong(m.getFields().get(5)[0]), 
				  (int)MathFP.toLong(m.getFields().get(6)[0])
				 );
			s.repaint(100);	
		}

		protected void addPanel(int acid)
		{
			SvInfoPanel panel=new SvInfoPanel();
			if (panels.size()==0) selected=acid;
			panels.put(acid,panel);
			panel.setSize(this.getWidth()-15, this.getHeight());
			panel.setBackground(this.getBackground());
			panel.setVisible(false);
			panel.addComponentListener(this);
			this.add(panel);
			scroller.setValues(0, 10, 0, 0);
			scroller.setSize(15,this.getHeight());
			scroller.setLocation(this.getWidth()-scroller.getWidth(), 0);
			scroller.setUnitIncrement(10);
			selectAc(acid);
		}
		
	    // These methods allows classes to register for MyEvents
	    public void addActionListener(ActionListener listener) {
	    	listenerList.add(ActionListener.class, listener);
	   }

	    // This methods allows classes to unregister for MyEvents
	   public void removeActionListener(ActionListener listener) {
		   listenerList.remove(ActionListener.class, listener);
	   }
	   
	   // This protected class is used to fire MyEvents
/*	   protected void fireActionEvent(ActionEvent evt) {
	       Object[] listeners = listenerList.getListenerList();
	       // Each listener occupies two elements - the first is the listener class
	       // and the second is the listener instance
	       for (int i=0; i<listeners.length; i+=2) {
	           if (listeners[i]==ActionListener.class) {
	               ((ActionListener)listeners[i+1]).actionPerformed(evt);
	           }
	       }
	   }	*/
	   
		public float getAlignmentY()
		{
			return Component.BOTTOM_ALIGNMENT;
		}
	   
		public void paint(Graphics g) {
			g.setColor(getBackground());
			g.fillRect(0,0,getWidth(),getHeight());
			super.paint(g);
		}


		public void componentHidden(ComponentEvent arg0) {
			
		}


		public void componentMoved(ComponentEvent arg0) {
			
		}


		public void componentResized(ComponentEvent arg0) {
			if (panels.isEmpty()) return;
			//System.out.println("resized");
			SvInfoPanel panel=panels.get(selected);
			int diff=panel.getHeight()-this.getHeight();
			
			if (arg0.getComponent().getClass().equals(SvInfoPanel.class))
			{
				scroller.setMaximum(diff>0?diff:0);
				scroller.setUnitIncrement(1);
			} else if (arg0.getComponent().equals(this)) {
				panel.setSize(this.getWidth()-15,panel.getHeight());
				scroller.setSize(15,this.getHeight());
				scroller.setLocation(this.getWidth()-scroller.getWidth(), 0);
				scroller.setMaximum(diff>0?diff:0);
				scroller.setUnitIncrement(10);
//				if (panel.getComponentCount()>0) scroller.setUnitIncrement(panel.getComponent(0).getHeight());

			}
			
		}

		public void componentShown(ComponentEvent arg0) {
		}

		public void adjustmentValueChanged(AdjustmentEvent arg0) {
			SvInfoPanel panel=panels.get(selected);
			if (arg0.getSource()!=scroller || panel.getComponentCount()==0) return;
			panel.setLocation(0, -scroller.getValue());
			repaint(100);
		}   
	
		public void actionPerformed(ActionEvent arg0) {
			if (arg0.getSource().getClass() == AcInfoContainer.class)
				selectAc(((AcInfoContainer) arg0.getSource()).getSelected());
		}		
		
		public void selectAc(int ac)
		{
			SvInfoPanel panel=panels.get(selected);
			if (panel==null) return;
			panel.setVisible(false);
			panel=panels.get(ac);
			if (panel==null) return;
			int dH=panel.getHeight()-this.getHeight();
			scroller.setValues(-panel.getY(), 1, 0, dH>0?dH:0);
			panel.setVisible(true);
			selected=ac;
			//System.out.println("selected");
		}
		
		
} //class

	
	
	

